﻿//******************************************************************************************************
//  SampleFileCleaner.cs - Gbtc
//
//  Copyright © 2010, Grid Protection Alliance.  All Rights Reserved.
//
//  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
//  the NOTICE file distributed with this work for additional information regarding copyright ownership.
//  The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
//  not use this file except in compliance with the License. You may obtain a copy of the License at:
//
//      http://www.opensource.org/licenses/eclipse-1.0.php
//
//  Unless agreed to in writing, the subject software distributed under the License is distributed on an
//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
//  License for the specific language governing permissions and limitations.
//
//  Code Modification History:
//  ----------------------------------------------------------------------------------------------------
//  09/24/2010 - Stephen C. Wills
//       Generated original version of source code.
//
//******************************************************************************************************

using System.IO;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Soap;
using TVA.IO;
using TVA.PhasorProtocols;
using TVA.PhasorProtocols.IeeeC37_118;

namespace TestBenchApi
{
    public class SampleFileCleaner
    {

        #region [ Members ]

        // Fields

        private string m_sampleFilePath;
        private string m_dataFilePath;
        private string m_configFilePath;
        private bool m_alreadyClean;

        #endregion

        #region [ Constructors ]

        /// <summary>
        /// Creates a new instance of the <see cref="SampleFileCleaner"/> class.
        /// </summary>
        /// <param name="sampleFilePath">The path to the sample file.</param>
        public SampleFileCleaner(string sampleFilePath)
        {
            m_sampleFilePath = sampleFilePath;
        }

        #endregion

        #region [ Properties ]

        /// <summary>
        /// Gets or sets the file path of the sample file.
        /// </summary>
        public string SampleFilePath
        {
            get
            {
                return m_sampleFilePath;
            }
            set
            {
                m_sampleFilePath = value;
                m_dataFilePath = null;
                m_configFilePath = null;
                m_alreadyClean = false;
            }
        }

        /// <summary>
        /// Gets the file path of the data file.
        /// </summary>
        public string DataFilePath
        {
            get
            {
                return m_dataFilePath;
            }
        }

        /// <summary>
        /// Gets the file path of the config frame.
        /// </summary>
        public string ConfigFilePath
        {
            get
            {
                return m_configFilePath;
            }
        }

        /// <summary>
        /// Gets a boolean indicating whether the sample file was already clean.
        /// </summary>
        public bool AlreadyClean
        {
            get
            {
                return m_alreadyClean;
            }
        }

        #endregion

        #region [ Methods ]

        /// <summary>
        /// Cleans the sample file.
        /// </summary>
        public void Clean()
        {
            SampleFileCleaner cleaner = SampleFileCleaner.Clean(m_sampleFilePath);
            m_dataFilePath = cleaner.DataFilePath;
            m_configFilePath = cleaner.ConfigFilePath;
            m_alreadyClean = cleaner.AlreadyClean;
        }

        /// <summary>
        /// Creates the file that stores the serialized configuration frame.
        /// </summary>
        /// <param name="configFrame">The configuration frame to be serialized.</param>
        public void CreateConfigFile(IConfigurationFrame configFrame)
        {
            FileStream configFileStream = null;

            if (m_configFilePath == null)
            {
                string appDir = FilePath.GetApplicationDataFolder();
                string fileName = Path.GetFileName(m_sampleFilePath);
                string configFileName = fileName.Substring(0, fileName.LastIndexOf('.')) + ".config.xml";
                m_configFilePath = appDir + '\\' + configFileName;

                if (!Directory.Exists(appDir))
                    Directory.CreateDirectory(appDir);

                if (File.Exists(m_configFilePath))
                    File.Delete(m_configFilePath);
            }

            try
            {
                SoapFormatter xmlSerializer = new SoapFormatter();

                configFileStream = File.Open(m_configFilePath, FileMode.Create, FileAccess.Write);
                xmlSerializer.AssemblyFormat = FormatterAssemblyStyle.Simple;
                xmlSerializer.TypeFormat = FormatterTypeStyle.TypesWhenNeeded;

                xmlSerializer.Serialize(configFileStream, configFrame);
            }
            finally
            {
                if (configFileStream != null)
                    configFileStream.Close();
            }
        }

        /// <summary>
        /// Writes data to the data file. If the data file does not yet exist, it is created in the temp directory.
        /// </summary>
        /// <param name="data">The data to be written to the data file.</param>
        public void WriteToDataFile(byte[] data, int offset, int count)
        {
            FileStream dataFileStream = null;

            if (m_dataFilePath == null)
            {
                string appDir = FilePath.GetApplicationDataFolder();
                string fileName = Path.GetFileName(m_sampleFilePath);
                m_dataFilePath = appDir + '\\' + fileName;

                if (!Directory.Exists(appDir))
                    Directory.CreateDirectory(appDir);

                if (File.Exists(m_dataFilePath))
                    File.Delete(m_dataFilePath);
            }

            try
            {
                dataFileStream = File.Open(m_dataFilePath, FileMode.Append, FileAccess.Write);
                dataFileStream.Write(data, offset, count);
            }
            finally
            {
                if (dataFileStream != null)
                    dataFileStream.Close();
            }
        }

        #endregion

        #region [ Static ]

        // Static Fields

        private static SampleFileCleaner s_sampleFileCleaner;

        // Static Methods

        /// <summary>
        /// Cleans the sample file located at the given path.
        /// </summary>
        /// <param name="sampleFilePath">The path to the sample file.</param>
        /// <returns>A <see cref="SampleFileCleaner"/> object that contains the names of the cleaned data file and configuration file.</returns>
        public static SampleFileCleaner Clean(string sampleFilePath)
        {
            FileStream sampleFileStream = null;

            try
            {
                sampleFileStream = File.OpenRead(sampleFilePath);
                return Clean(sampleFilePath, sampleFileStream);
            }
            finally
            {
                if (sampleFileStream != null)
                    sampleFileStream.Close();
            }
        }

        /// <summary>
        /// Cleans the sample file data.
        /// </summary>
        /// <param name="sampleFileStream">The stream containing the sample data.</param>
        /// <returns>A <see cref="SampleFileCleaner"/> object that contains the names of the cleaned data file and configuration file.</returns>
        public static SampleFileCleaner Clean(Stream sampleFileStream)
        {
            return Clean(Path.GetRandomFileName(), sampleFileStream);
        }

        /// <summary>
        /// Cleans the sample file data.
        /// </summary>
        /// <param name="filePath">The file path used to generate the data file path and configuration file path.</param>
        /// <param name="sampleFileStream">The stream containing the sample data.</param>
        /// <returns>A <see cref="SampleFileCleaner"/> object that contains the names of the cleaned data file and configuration file.</returns>
        public static SampleFileCleaner Clean(string filePath, Stream sampleFileStream)
        {
            FrameParser parser = null;

            try
            {
                byte[] buffer = new byte[32768];
                int dataLen;

                s_sampleFileCleaner = new SampleFileCleaner(filePath);
                s_sampleFileCleaner.m_alreadyClean = true;

                parser = new FrameParser(DraftRevision.Draft7);
                parser.ReceivedFrameBufferImage += Parser_ReceivedFrameBufferImage;
                parser.ReceivedConfigurationFrame += Parser_ReceivedConfigurationFrame;
                parser.Start();

                do
                {
                    dataLen = sampleFileStream.Read(buffer, 0, buffer.Length);
                    parser.Write(buffer, 0, dataLen);
                } while (dataLen > 0);
            }
            finally
            {
                if (parser != null)
                {
                    parser.ReceivedFrameBufferImage -= Parser_ReceivedFrameBufferImage;
                    parser.ReceivedConfigurationFrame -= Parser_ReceivedConfigurationFrame;
                    parser.Stop();
                }
            }

            return s_sampleFileCleaner;
        }

        // Handles data frames parsed by the frame parser.
        private static void Parser_ReceivedFrameBufferImage(object sender, TVA.EventArgs<TVA.PhasorProtocols.FundamentalFrameType, byte[], int, int> e)
        {
            if (e.Argument1 == FundamentalFrameType.DataFrame)
                s_sampleFileCleaner.WriteToDataFile(e.Argument2, e.Argument3, e.Argument4);
            else
                s_sampleFileCleaner.m_alreadyClean = false;
        }

        // Handles configuration frames parsed by the frame parser.
        private static void Parser_ReceivedConfigurationFrame(object sender, TVA.EventArgs<IConfigurationFrame> e)
        {
            s_sampleFileCleaner.CreateConfigFile(e.Argument);
        }

        #endregion
        
    }
}
